package com.zxhysy.log.annotation.advisor;

import com.zxhysy.log.annotation.Log;
import com.zxhysy.log.annotation.LogConfig;
import com.zxhysy.log.annotation.LogIgnore;
import com.zxhysy.log.annotation.entity.LogRecord;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.lang.NonNull;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 日志注解解析器
 * @author zhangxinhong
 * @since 2021/9/22 11:19
 */
public class LogAnnotationParser implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * 类解析 这里一般是方法上面没找到注解，就会用类的注解
     * @param type 注解类
     * @return logOperations
     */
    @NonNull
    public Collection<LogRecord> parseLogAnnotations(Class<?> type) {
        DefaultLogConfig defaultLogConfig = new DefaultLogConfig(type);
        return parseLogAnnotations(defaultLogConfig, type);
    }

    /**
     * 方法解析
     * @param method 目标方法
     * @return logOperations
     */
    @NonNull
    public Collection<LogRecord> parseLogAnnotations(Method method) {
        // 如果以后存在多个注解，可以在这里改 返回 Collection<LogRecord>
        DefaultLogConfig defaultLogConfig = new DefaultLogConfig(method.getDeclaringClass());
        return parseLogAnnotations(defaultLogConfig, method);
    }

    /**
     * 解析注解元素
     * @param defaultLogConfig 默认配置
     * @param ae               注解元素 可能是 method\class
     * @return LogOperations
     */
    @NonNull
    private Collection<LogRecord> parseLogAnnotations(DefaultLogConfig defaultLogConfig, AnnotatedElement ae) {
        Collection<LogRecord> ops = parseLogAnnotations(defaultLogConfig, ae, false);
        // 如果存在多个操作，就
        if (ops.size() > 1) {
            // 找到多个操作->本地声明覆盖接口声明的操作
            Collection<LogRecord> localOps = parseLogAnnotations(defaultLogConfig, ae, true);
            if (!localOps.isEmpty()) {
                return localOps;
            }
        }
        return ops;
    }

    /**
     * 解析注解元素
     * @param defaultLogConfig 默认配置
     * @param ae               注解元素 可能是 method\class
     * @param localOnly        find\get 默认 get模式：只获取当前类的方法和接口上的，find 则桥接模式也会获取到，桥接模式是泛型参数方法编译产生的一个方法
     * @return LogOperations
     */
    @NonNull
    private Collection<LogRecord> parseLogAnnotations(DefaultLogConfig defaultLogConfig, AnnotatedElement ae, boolean localOnly) {
        // findMergedAnnotation 方法可找出父类和接口、父类方法和接口方法上的注解，并可以处理桥接方法，实现一键找到继承链的注解
        // getAllMergedAnnotations 对应 SearchStrategy.INHERITED_ANNOTATIONS：
        Collection<? extends Annotation> annotations = (localOnly ?
                AnnotatedElementUtils.getAllMergedAnnotations(ae, Log.class) :
                AnnotatedElementUtils.findAllMergedAnnotations(ae, Log.class));

        if (ObjectUtils.isEmpty(annotations)) {
            return Collections.emptyList();
        }

        // 解析注解
        return annotations.stream()
                .filter(Log.class::isInstance)
                .map(Log.class::cast)
                .map(annotation -> parseLogAnnotation(defaultLogConfig, annotation))
                .collect(Collectors.toList());
    }

    /**
     * 解析logable注解
     * @param defaultLogConfig 默认配置
     * @param annotation       注解
     * @return logableOperation
     */
    private LogRecord parseLogAnnotation(DefaultLogConfig defaultLogConfig, Log annotation) {
        LogRecord logRecord = LogRecord.builder().build();

        logRecord.setBizType(annotation.bizType());
        logRecord.setLogType(annotation.logType());
        logRecord.setBizId(annotation.bizId());
        logRecord.setDescription(annotation.description());
        logRecord.setSuccess(annotation.success());
        logRecord.setFail(annotation.fail());
        logRecord.setOperator(annotation.operator());
        logRecord.setIgnore(annotation.ignore());
        // 执行默认配置
        defaultLogConfig.applyDefault(logRecord);
        return logRecord;
    }

    /**
     * 默认配置
     */
    private static class DefaultLogConfig {

        /**
         * 目标类
         */
        private final Class<?> target;

        /**
         * 默认配置
         */
        private String bizType;

        /**
         * 日志类型
         */
        private String logType;

        /**
         * 是否初始化完毕
         */
        private boolean initialized = false;

        public DefaultLogConfig(Class<?> target) {
            this.target = target;
        }

        public void applyDefault(@NonNull LogRecord logRecord) {
            // 初始化配置
            if (!this.initialized) {
                LogConfig annotation = AnnotatedElementUtils.findMergedAnnotation(this.target, LogConfig.class);
                if (annotation != null) {
                    this.bizType = annotation.bizType();
                    this.logType = annotation.logType();
                }
                this.initialized = true;
            }

            // 全局配置对不同的注解的附赠解析
            // 执行赋值
            if (!StringUtils.hasText(logRecord.getBizType())) {
                logRecord.setBizType(this.bizType);
            }

            // 执行赋值
            if (!StringUtils.hasText(logRecord.getLogType())) {
                logRecord.setLogType(this.logType);
            }
        }
    }
}
